/**
 * Redis 连接池模块
 * @author HalRui
 * @version 1.0.0
 * @description Redis连接池管理器，提供独立的Redis连接管理功能
 * @date 2025年9月27日
 */

const { createClient } = require('redis');
const config = require('../env.config.json');

class RedisPool {
    constructor() {
        this.redisClient = null;
        this.isRedisConnected = false;
    }

    /**
     * 连接 Redis（使用连接池）
     */
    async connectRedis() {
        if (this.redisClient && this.isRedisConnected) {
            console.log('Redis 已连接，跳过...');
            return this.redisClient;
        }

        try {
            // Redis 连接配置（支持连接池）
            const redisConfig = {
                url: config.redis.url || 'redis://localhost:6379',
                socket: {
                    connectTimeout: 10000,
                    lazyConnect: false, // 立即连接
                    keepAlive: true,
                    reconnectStrategy: (retries) => {
                        if (retries > 10) {
                            console.error('Redis 重连失败次数过多，停止重连');
                            return new Error('Too many retries');
                        }
                        console.log(`Redis 重连尝试 ${retries}`);
                        return Math.min(retries * 50, 500); // 指数退避，最大500ms
                    }
                },
                pool: {
                    min: 2,  // 最小连接数
                    max: 10, // 最大连接数
                    acquireTimeoutMillis: 30000,
                    createTimeoutMillis: 30000,
                    destroyTimeoutMillis: 5000,
                    idleTimeoutMillis: 30000,
                    reapIntervalMillis: 1000,
                    createRetryIntervalMillis: 200,
                }
            };

            this.redisClient = createClient(redisConfig);

            // 监听 Redis 事件
            this.redisClient.on('connect', () => {
            console.log('🔄 Redis 连接中...');
        });

            this.redisClient.on('ready', () => {
                this.isRedisConnected = true;
                console.log('✅ Redis 连接成功！');
            });

            this.redisClient.on('error', (err) => {
                console.error('❌ Redis 错误:', err.message);
                this.isRedisConnected = false;
            });

            this.redisClient.on('end', () => {
                this.isRedisConnected = false;
                console.log('✅ Redis 连接已关闭');
            });

            this.redisClient.on('disconnect', () => {
                this.isRedisConnected = false;
                console.log('📡 Redis 连接已断开');
            });

            // 建立连接
            await this.redisClient.connect();
            
            // 测试连接
            await this.redisClient.ping();
            console.log('🎯 Redis PING 测试通过');

            return this.redisClient;

        } catch (error) {
            console.error('❌ Redis 连接失败:', error);
            throw error;
        }
    }

    /**
     * 获取 Redis 客户端
     */
    getRedisClient() {
        if (!this.redisClient || !this.isRedisConnected) {
            throw new Error('Redis 未连接，请先调用 connectRedis()');
        }
        return this.redisClient;
    }

    /**
     * 切换到指定数据库
     */
    async selectDB(dbNumber) {
        const client = this.getRedisClient();
        try {
            await client.select(dbNumber);
            return true;
        } catch (error) {
            console.error(`切换到数据库 ${dbNumber} 失败:`, error);
            throw error;
        }
    }

    /**
     * 保存邮箱验证码
     */
    async saveEmailCode(email, code, ttl = 15 * 60) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.emailDb);
            return await client.setEx(email, ttl, code);
        } catch (error) {
            console.error(`保存邮箱验证码失败 [${email}]:`, error);
            throw new Error('保存邮箱验证码失败');
        }
    }

    /**
     * 获取邮箱验证码
     */
    async getEmailCode(email) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.emailDb);
            return await client.get(email);
        } catch (error) {
            console.error(`获取邮箱验证码失败 [${email}]:`, error);
            throw new Error('获取邮箱验证码失败');
        }
    }

    /**
     * 保存验证码
     */
    async setCaptcha(uuid, captcha, ttl = 300) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.captchaDb);
            return await client.setEx(uuid, ttl, captcha);
        } catch (error) {
            console.error(`保存验证码失败 [${uuid}]:`, error);
            throw new Error('保存验证码失败');
        }
    }

    /**
     * 获取验证码
     */
    async getCaptcha(uuid) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.captchaDb);
            return await client.get(uuid);
        } catch (error) {
            console.error(`获取验证码失败 [${uuid}]:`, error);
            throw new Error('获取验证码失败');
        }
    }

    /**
     * 保存刷新令牌
     */
    async setRefreshToken(token, ttl = 60 * 60 * 24) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.userDb);
            return await client.setEx(token, ttl, '');
        } catch (error) {
            console.error(`保存刷新令牌失败:`, error);
            throw new Error('保存刷新令牌失败');
        }
    }

    /**
     * 检查令牌是否存在
     */
    async isExists(token) {
        const client = this.getRedisClient();
        try {
            await this.selectDB(config.redis.userDb);
            const ttl = await client.ttl(token);
            
            // TTL 返回值说明:
            // -2: 键不存在
            // -1: 键存在但没有设置过期时间
            // >=0: 剩余的生存时间(秒)
            if (ttl === -2) {
                return { expired: true, reason: '键不存在' };
            }
            if (ttl === -1) {
                return { expired: false, reason: '键永不过期' };
            }
            if (ttl === 0) {
                return { expired: true, reason: '键已过期' };
            }

            return {
                expired: false,
                ttl: ttl,
                expiresIn: `${ttl} 秒后过期`
            };
        } catch (error) {
            console.error(`检查令牌状态失败:`, error);
            throw new Error('检查令牌状态失败');
        }
    }

    /**
     * 设置键值对
     */
    async set(key, value, ttl = null) {
        const client = this.getRedisClient();
        try {
            if (ttl) {
                return await client.setEx(key, ttl, value);
            }
            return await client.set(key, value);
        } catch (error) {
            console.error(`Redis SET 错误 [${key}]:`, error);
            throw error;
        }
    }

    /**
     * 获取键值
     */
    async get(key) {
        const client = this.getRedisClient();
        try {
            return await client.get(key);
        } catch (error) {
            console.error(`Redis GET 错误 [${key}]:`, error);
            throw error;
        }
    }

    /**
     * 删除键
     */
    async del(key) {
        const client = this.getRedisClient();
        try {
            return await client.del(key);
        } catch (error) {
            console.error(`Redis DEL 错误 [${key}]:`, error);
            throw error;
        }
    }

    /**
     * 设置哈希字段
     */
    async hset(key, field, value) {
        const client = this.getRedisClient();
        try {
            return await client.hSet(key, field, value);
        } catch (error) {
            console.error(`Redis HSET 错误 [${key}, ${field}]:`, error);
            throw error;
        }
    }

    /**
     * 健康检查
     */
    async healthCheck() {
        try {
            const client = this.getRedisClient();
            const start = Date.now();
            await client.ping();
            const latency = Date.now() - start;
            
            return {
                status: 'healthy',
                latency: latency,
                host: client.options?.socket?.host || 'localhost',
                port: client.options?.socket?.port || 6379
            };
        } catch (error) {
            console.error('Redis 健康检查失败:', error);
            return {
                status: 'unhealthy',
                error: error.message
            };
        }
    }

    /**
     * 断开连接
     */
    async disconnect() {
        if (this.redisClient && this.isRedisConnected) {
            try {
                await this.redisClient.disconnect();
                this.isRedisConnected = false;
                console.log('✅ Redis 连接已关闭');
            } catch (error) {
                console.error('❌ 关闭 Redis 连接失败:', error);
                throw error;
            }
        }
    }
}

module.exports = RedisPool;